package com.dbflow5.query;

import com.dbflow5.SqlUtils;
import com.dbflow5.config.FlowLog;
import com.dbflow5.database.DatabaseStatement;
import com.dbflow5.database.DatabaseStatementWrapper;
import com.dbflow5.database.DatabaseWrapper;
import com.dbflow5.database.FlowCursor;
import com.dbflow5.database.SQLiteException;
import com.dbflow5.runtime.NotifyDistributor;
import com.dbflow5.structure.ChangeAction;

/**
 * Description: Base implementation of something that can be queried from the database.
 */
public abstract class BaseQueriable<TModel> implements Queriable, Actionable {
    public Class<TModel> table;

    protected BaseQueriable(Class<TModel> table){
        this.table = table;
    }

    @Override
    public abstract ChangeAction primaryAction();

    @Override
    public long longValue(DatabaseWrapper databaseWrapper) {
        try {
            String query = getQuery();
            FlowLog.log(FlowLog.Level.V, "Executing query: "+query+"");
            return SqlUtils.longForQuery(databaseWrapper, query);
        } catch (SQLiteException sde) {
            // catch exception here, log it but return 0;
            FlowLog.logWarning(sde);
        }

        return 0;
    }

    @Override
    public String stringValue(DatabaseWrapper databaseWrapper) {
        try {
            String query = getQuery();
            FlowLog.log(FlowLog.Level.V, "Executing query: "+query);
            return SqlUtils.stringForQuery(databaseWrapper, query);
        } catch (SQLiteException sde) {
            // catch exception here, log it but return null;
            FlowLog.logWarning(sde);
        }

        return null;
    }

    @Override
    public boolean hasData(DatabaseWrapper databaseWrapper) {
        return longValue(databaseWrapper) > 0;
    }

    @Override
    public FlowCursor cursor(DatabaseWrapper databaseWrapper) {
        if (primaryAction() == ChangeAction.INSERT) {
            // inserting, let's compile and insert
            compileStatement(databaseWrapper).executeInsert();
        } else {
            String query = getQuery();
            FlowLog.log(FlowLog.Level.V, "Executing query: " + query);
            databaseWrapper.execSQL(query);
        }
        return null;
    }

    @Override
    public long executeInsert(DatabaseWrapper databaseWrapper) {
        return compileStatement(databaseWrapper).executeInsert();
    }

    @Override
    public void execute(DatabaseWrapper databaseWrapper) {
        FlowCursor cursor = cursor(databaseWrapper);
        if (cursor != null) {
            cursor.close();
        } else {
            // we dont query, we're executing something here.
            NotifyDistributor.get().notifyTableChanged(table, primaryAction());
        }
    }

    @Override
    public DatabaseStatement compileStatement(DatabaseWrapper databaseWrapper) {
        String query = getQuery();
        FlowLog.log(FlowLog.Level.V, "Compiling Query Into Statement: " + query);
        return new DatabaseStatementWrapper<>(databaseWrapper.compileStatement(query), this);
    }

    @Override
    public String toString() {
        return getQuery();
    }
}
